//
//  Environment.cpp
//  AntSimulation
//


#include "Environment.h"
#include "Creator.h"
#include <time.h>
using namespace std;

// statische Variablen initialisieren, ansonsten in Scope nicht nutzbar
Environment* Environment::pInstance = NULL;
int Environment::nCurM = 0;
int Environment::nCurN = 0;
int Environment::nAreaCounter =0;
Area* Environment::pEntry = NULL;

Environment::Environment() {};

Environment::~Environment() {};

Environment* Environment::getInstance(int m, int n) {
    // Environment erstellen falls noch nicht erledigt
    if(pInstance == NULL) {
        pInstance = new Environment();
    }
    // Entry-point erstellen falls noch nicht erledigt
    if(pEntry == NULL) {
        pEntry = Creator::Create_Area();
    }
    if(m!=0 && n!=0) {
        // keine Groessenevaluierung falls 0 im Parameter. Gewuenschter Aufruf ohne Evaluierung!
        if(nCurM<=m && nCurN<=n) {
            // Expand falls aktuelle Groesse nicht gleich gewuenschter Groesse
            pInstance->Expand(m,n);
        }
    }
    return (pInstance);
}

int Environment::getCurM() {
    return nCurM;
}

int Environment::getCurN() {
    return nCurN;
}

void Environment::Expand(int m, int n) {
    if(m==0 || n==0) return; // nothing doable
    if(m==nCurM && n==nCurN) return; // nothing todo - size fits already
    
    // Expand
    // get starting point
    Area* pArea = pEntry;



    // Ersten in Zeile merken
    Area* pFirstInRow = pEntry;
    if(pArea == NULL) return; // entry-point seems to be garbage
    // Spalten erweitern, wenn noetig
    for (int jj=1; jj<n; jj++) {
        // nach Osten gehen, sofern moeglich
        if(pArea->get_east() !=NULL) {
            // weitergehen
            pArea = pArea->get_east();
        } else {
            // Osten erzeugen
            Area* pAreaNew = Creator::Create_Area();

            // Area einfuegen
            AddHorizontal(pArea,pAreaNew);
            // weitergehen
            pArea = pAreaNew;

			
        }
    }
    for (int ii=1; ii<m; ii++) {
    	// Zeile erweitern, wenn noetig
        // Zeiger auf ersten in Zeile setzen
        pArea = pFirstInRow;
        // nach Sueden gehen, sofern moeglich
    	if(pArea->get_south() != NULL) {
            // weitergehen
    		pArea = pArea->get_south();
            // ersten in Zeile merken
            pFirstInRow = pArea;
    	} else {
            // Sueden erzeugen
    		Area* pAreaNew = Creator::Create_Area();
            // Area einfuegen
            AddVertical(pArea, pAreaNew);
            // ersten in Zeile merken
            pFirstInRow = pAreaNew;
            // weitergehen
            pArea = pAreaNew;
    	}
        // Spalten erweitern, wenn noetig
    	for (int jj=1; jj<n; jj++) {
            // nach Osten gehen, sofern moeglich
    		if(pArea->get_east() !=NULL) {
                // weitergehen
    			pArea = pArea->get_east();
    		} else {
                // Osten erzeugen
				Area* pAreaNew = Creator::getInstance()->Create_Area();
                // Area einfuegen
                AddHorizontal(pArea,pAreaNew);
                // weitergehen
                pArea = pAreaNew;
    		}
    	}
    }
    // set new size
    nCurM = m;
    nCurN = n;
}

Area* Environment::getEntryArea() {
	return pEntry;
}	

void Environment::AddHorizontal(Area* pwest, Area* peast) {
    // set west in new
    peast->set_west(pwest);
    // east in western neighbor
    pwest->set_east(peast);
    // vom westlichen Knoten nach Norden
    Area* pTemp = pwest->get_north();
    if(pTemp !=NULL) {
        // vom Norden in den Osten, direkt noerdlich der neuen Area
        pTemp = pTemp->get_east();
        if(pTemp != NULL) {
            // set north in new
            peast->set_north(pTemp);
            // south in northern neighbor
            pTemp->set_south(peast);
        }
    }
	
	//Zum Testen: food auf feld  hinzu
	//if(peast->getNumber()==20){peast->AddToListFront(Creator::Create_Food(100));}
}

void Environment::AddVertical(Area* pnorth, Area* psouth) {
    // set north in new
    psouth->set_north(pnorth);
    // set south in old
    pnorth->set_south(psouth);    
}

void Environment::PrintEnvironment() {
    // Zeiger auf Einstiegspunkt setzen
    Area* pArea = pEntry;
    // Zeiger auf ersten in der Reihe merken
    Area* pFirstInRow = pEntry;
    while (pArea != NULL) {
        // Area ausgeben
        pArea->PrintInfo();
        // nach Osten bewegen, sofern möglich
        pArea = pArea->get_east();
        if(pArea == NULL) {
            // Osten ist NULL, in nächste Zeile wechseln
            pArea = pFirstInRow->get_south();
            pFirstInRow = pArea;
        }
    }
}


void Environment::jPrintEnvironment()
{
	Area* pArea = pEntry;
	Area* pFirstInRow=pEntry;
	int phlevel=0;
	int totalfood=0;
	int totalants=0;

	while(pArea!=NULL)
	{
		//print horizontal line upside the areas
		cout<<endl;
		for(int i=0;i<5*5;++i){cout<<"-";}
		cout<<endl;

		int rowcount=0;

		while(pArea!=NULL)
		{
			
			cout<<"|";
			
			
			int hill=0;
			int ants=0;
			int food=0;
			
			bool empty=false;
			phlevel=pArea->get_pheromonelevel();
			for(pArea->itemiterator=pArea->items.begin();pArea->itemiterator!=pArea->items.end();(pArea->itemiterator)++)
			{
				//std::cout<<pArea->getNumber()<<"|"<<pArea->getName()<<"\t";
				if(typeid(*(*(pArea->itemiterator))).name()==typeid(Anthill).name())
				{
					++hill;
				}
				else if(typeid(*(*(pArea->itemiterator))).name()==typeid(Ant).name())
				{
					++ants;//ants in the list of this area
					++totalants;//total ants living in the current round
				}
				else if(typeid(*(*(pArea->itemiterator))).name()==typeid(Food).name())
				{
					++food;
					Food* thefood=dynamic_cast<Food*>(*(pArea->itemiterator));
					totalfood+=thefood->get_amount();
					if(totalfood<=0)
					{
						//delete food;
					}
				}
				else
				{
					empty=true;
				}
			}
					
			if(hill>0)
			{
				if(food>0)
				{
					if(ants==1){cout<<phlevel<<"Af.";}
					else if (ants>1){cout<<phlevel<<"Af:";}
					else {cout<<phlevel<<"Af ";}
				}
				else
				{
					if(ants==1){cout<<phlevel<<"A .";}
					else if (ants>1){cout<<phlevel<<"A :";}
					else {cout<<phlevel<<"A  ";}
				}
			}
			else if(food>0)
			{
				if(ants==1){cout<<phlevel<<" f.";}
				else if(ants>1){cout<<phlevel<<" f:";}
				else{cout<<phlevel<<" f ";}
			}
			else if(ants==1){cout<<phlevel<<"  .";}
			else if(ants>1){cout<<phlevel<<"  :";}
			else {cout<<phlevel<<"   ";}
		
			
			
			rowcount++;
			if(rowcount==this->getCurN()){cout<<"|";}
			pArea=pArea->get_east();
			
		}
		cout<<endl;
		pFirstInRow=pFirstInRow->get_south();
		pArea=pFirstInRow;
		
	}
	cout<<endl<<"Total food: "<<totalfood<<endl;
	cout<<"Total ants: "<<totalants<<endl;
}


void Environment::PrintEnvironmentViewable() {
    // Zeiger auf Einstiegspunkt setzen
    Area* pArea = pEntry;
    // Zeiger auf ersten in der Reihe merken
    Area* pFirstInRow = pEntry;
    while (pArea != NULL) {
        // Ausgabe Nord-Informationen
        while (pArea != NULL) {
            // Area ausgeben
            pArea->PrintViewable('n');
            // nach Osten bewegen, sofern möglich
            pArea = pArea->get_east();

        }
        std::cout << "\n";
        // Ausgabe Center-Informationen
        pArea = pFirstInRow;
        while (pArea != NULL) {
            // Area ausgeben
            pArea->PrintViewable('c');
            // nach Osten bewegen, sofern möglich
            pArea = pArea->get_east();
            
        }
        std::cout << "\n";
        // Ausgabe Sued-Informationen
        pArea = pFirstInRow;
        while (pArea != NULL) {
            // Area ausgeben
            pArea->PrintViewable('s');
            // nach Osten bewegen, sofern möglich
            pArea = pArea->get_east();
            
        }
        std::cout << "\n";
        // Naechste Zeile
        if(pArea == NULL) {
            // Osten ist NULL, in nächste Zeile wechseln
            pArea = pFirstInRow->get_south();
            pFirstInRow = pArea;
        }
    }
}



void Environment::act()
{
	Area* pArea = pEntry;
	Area* pFirstInRow=pEntry;
	while(pArea!=NULL)
	{
		while(pArea!=NULL)
		{
			pArea->act();
			pArea=pArea->get_east();
		}
		//std::cout<<std::endl;
		pFirstInRow=pFirstInRow->get_south();
		pArea=pFirstInRow;
	}


}

int Environment::init_Env(int cntAnthill, int cntFoodsource, int cntStartFood)
{
	srand(time(NULL));
	//srand(3);
	int anthillpos=1+rand()%(Creator::getInstance()->get_nAreasCreated());
	std::list<unsigned int> foodsources;

	int i=0;
	while(i<cntFoodsource){
		unsigned int foodpos=1+rand()%(Creator::getInstance()->get_nAreasCreated());
		cout<<endl<<"anthillpos: "<<anthillpos<<" foodpos: "<<foodpos<<endl;
		foodsources.push_front(foodpos);
		++i;
	}

	

	Area* pArea=Environment::pEntry;
	Area* pFirstInRow=pEntry;

	try{
		while(pArea!=NULL)
		{
			while(pArea!=NULL)
			{
				if(pArea->getNumber()==anthillpos)
				{
					//place anthill
					Anthill* newAnthill = Creator::getInstance()->Create_Anthill();
					newAnthill->set_foodcount(cntStartFood);
					newAnthill->set_anthillArea(pArea);//!!!!!
					pArea->items.push_front(newAnthill);
					cout<<"anthill placed";getchar();
				}
				for(std::list<unsigned int>::iterator it=foodsources.begin(); it!=foodsources.end();++it)
				{
					if(pArea->getNumber()==*it)
					{
						//place food
						Food* newFood = Creator::getInstance()->Create_Food(cntFoodsource);
						newFood->set_amount(100);
						pArea->items.push_front(newFood);
						cout<<"food placed";
						//getchar();
					}
				}


				pArea=pArea->get_east();
			}
			pFirstInRow=pFirstInRow->get_south();
			pArea=pFirstInRow;
		}
	}
	catch(...)
	{
		return -1;
	}
	return 0;

}



//
//int Environment::init_Env(int cntAnthill, int cntFoodsource, int cntStartFood)
//{
//    srand(time(NULL));
//
//    //create Anthill
//    //for (int i = 0; i < cntAnthill; i++)
//    //{
//        Anthill* newAnthill = Creator::getInstance()->Create_Anthill();
//        //Food* startFood = Creator::getInstance()->Create_Food(cntStartFood);
//		newAnthill->set_foodcount(cntStartFood);
//
//        if(newAnthill != NULL)
//        {
//            //x koordinate fÃ¼r Anthill
//            int place_m = 1+rand() % (getCurM());
//            int place_n = 1+rand() % (getCurN());
//
//
//
//
//			/////Fehler....
//
//
//            //place Anthill
//            // Zeiger auf Einstiegspunkt setzen
//            Area* pArea = pEntry;
//            while(pArea != NULL && place_m >0)
//            {
//                pArea = pArea->get_east();
//                place_m--;
//            }
//            while(pArea != NULL && place_n >0)
//            {
//                pArea = pArea->get_south();
//                place_n--;
//            }
//           // try{
//                pArea->AddToListFront(newAnthill);
//                //pArea->AddToListFront(startFood);
//            //}catch(...){ return -10; }//no Anthill was created
//        }
//        else
//        {
//            //Anthill and Food was created, but NOT linked
//            return -11;
//        }
//    //}
//        //create Foodsource
//   // for (int i = 0; i < cntFoodsource; i++)
//    //{
//        Food* newFoodsource = Creator::getInstance()->Create_Food(0);
//        newFoodsource->set_amount(cntFoodsource);
//
//        if(newFoodsource != NULL)
//        {
//            //x koordinate fuer Foodsource
//            int place_m = 1+rand() % (getCurM());
//            int place_n = 1+rand() % (getCurN());
//
//            //place Foodsource
//            // Zeiger auf Einstiegspunkt setzen
//            Area* pArea = pEntry;
//            while(pArea != NULL && place_m >0)
//            {
//                pArea = pArea->get_east();
//                place_m--;
//            }
//            while(pArea != NULL && place_n >0)
//            {
//                pArea = pArea->get_south();
//                place_n--;
//            }
//            //try{
//                pArea->AddToListFront(newFoodsource);
//				
//            //}catch(...){ return -20; }//no Foodsource was created
//        }
//        else
//        {
//            //Foodsource was created, but NOT linked
//            return -21;
//        }
//    //}
//    return 0;
//}